package controllers

import command.CommandExec
import dao._
import implicits.Implicits._
import play.api.libs.json.Json
import play.api.mvc.{AbstractController, Action, AnyContent, ControllerComponents}
import shared.VarTool
import tool.Pojo.{CommandData, ProportionData}
import tool.{FormTool, Tool}
import utils.Utils

import java.io.File
import javax.inject.Inject
import scala.collection.immutable.SeqMap
import scala.concurrent.ExecutionContext.Implicits.global

/**
 * Created by yz on 7/5/2021
 */
class CellController @Inject()(cc: ControllerComponents)(
  implicit val browseCellDao: BrowseCellDao,
  implicit val modeDao: ModeDao,
) extends AbstractController(cc) {

  val bloodFile = new File(Tool.dataDir, "cell_rel_blood_mean.csv")
  val tissueFile = new File(Tool.dataDir, "cell_rel_tissue_mean.csv")
  val kind = VarTool.cellKind

  def getHead1000 = Action.async {
    browseCellDao.selectHead10000.map { x =>
      val json = Utils.getJsonByTs(x)
      Ok(json)
    }
  }

  def getAllCellTypes = Action.async {
    browseCellDao.selectAllCellTypes.map { x =>
      val types = x.flatMap { x =>
        x.mySplit(";")
      }.map(_.trim).distinct
      Ok(Json.toJson(types))
    }
  }

  def getAllCellOrigins = Action.async {
    browseCellDao.selectAllCellOrigins.map { x =>
      val types = x.flatMap { x =>
        x.mySplit(";")
      }.map(_.trim).distinct
      Ok(Json.toJson(types))
    }
  }

  def search = Action.async { implicit request =>
    val data = FormTool.cellForm.bindFromRequest().get
    browseCellDao.selectByData(data).map { x =>
      val json = Utils.getJsonByTs(x)
      Ok(json)
    }
  }

  def downloadPlotData = Action { implicit request =>
    val id=FormTool.idForm.bindFromRequest().get.id
    val kind=FormTool.kindForm.bindFromRequest().get.kind
    val file=getInputFile(kind,id)
    Ok.sendFile(file).withHeaders(
      CONTENT_DISPOSITION -> ("attachment; filename=" + file.getName),
      CONTENT_TYPE -> "application/x-download"
    )
  }

  def downloadProportionPlotData = Action { implicit request =>
    val idOp=FormTool.idOpForm.bindFromRequest().get.id
    val kind=FormTool.kindForm.bindFromRequest().get.kind
    val file=idOp.map{id=>
      kind match {
        case VarTool.tissueKind =>new File(Tool.dataDir, s"cohorts/${id}_cell_rel_tissue.csv")
        case VarTool.bloodKind =>new File(Tool.dataDir, s"cohorts/${id}_cell_rel_blood.csv")
      }
    }.getOrElse{
      kind match {
        case VarTool.tissueKind =>tissueFile
        case VarTool.bloodKind =>bloodFile
      }
    }
    Ok.sendFile(file).withHeaders(
      CONTENT_DISPOSITION -> ("attachment; filename=" + file.getName),
      CONTENT_TYPE -> "application/x-download"
    )
  }

  def downloadProportionBrowseData = Action { implicit request =>
    val id=FormTool.idForm.bindFromRequest().get.id
    val file=new File(Tool.dataDir, s"cohorts/${id}_cell_rel_browse.csv")
    Ok.sendFile(file).withHeaders(
      CONTENT_DISPOSITION -> ("attachment; filename=" + file.getName),
      CONTENT_TYPE -> "application/x-download"
    )
  }

  def toResultByIdOrName = Action.async { implicit request =>
    val data = FormTool.idForm.bindFromRequest().get
    val kind = FormTool.kindForm.bindFromRequest().get.kind
    val id = data.id
    val ids = id.mySplit(",").map(_.trim).distinct
    browseCellDao.selectAllByName(ids).map { x =>
      val finalId = x.map(_.cellName).mkString(",")
      Ok(views.html.search.result(finalId, kind))
    }
  }

  def selectAllByName: Action[AnyContent] = Action.async { implicit request =>
    val data = FormTool.idForm.bindFromRequest().get
    val id = data.id
    val ids = id.mySplit(",").map(_.trim).distinct
    browseCellDao.selectAllByName(ids).map { x =>
      val json = Utils.getJsonByTs(x)
      Ok(json)
    }
  }

  def getAllName: Action[AnyContent] = Action.async { implicit request =>
    browseCellDao.selectAllName.map { names =>
      Ok(Json.toJson(names))
    }
  }

  def detailInfo: Action[AnyContent] = Action { implicit request =>
    val data = FormTool.idForm.bindFromRequest().get
    val tabOp = FormTool.tabOpForm.bindFromRequest().get.tab
    Ok(views.html.search.cellDetail(data.id, tabOp))
  }

  def getDetail: Action[AnyContent] = Action.async { implicit request =>
    val data = FormTool.idForm.bindFromRequest().get
    val id = data.id
    browseCellDao.selectByName(id).map { x =>
      val json = x.myAsJson
      Ok(json)
    }
  }

  def getInputFile(subKind:String, id: String) = {
    new File(Tool.dataDir, s"${kind}/expression_${subKind}/${id}.csv")
  }

  def getProfileImage = Action {
    implicit request =>
      val data = FormTool.profileForm.bindFromRequest().get
      val tmpDir = Tool.createTempDirectory("tmpDir")
      val absPdfFile = new File(tmpDir, "abs_out.pdf")
      val relPdfFile = new File(tmpDir, "rel_out.pdf")
      val absPngFile = new File(tmpDir, "abs_out.png")
      val relPngFile = new File(tmpDir, "rel_out.png")
      val command =
        s"""
           |${"Rscript".condaPath()} ${Tool.rPath.unixPath}/detail_cell_boxplot1.R  --t '${data.id}' --absInput '${getInputFile(VarTool.absKind,data.id).unixPath}' --absOut ${absPdfFile.unixPath} --c '${data.kinds.mkString(",")}' --relInput '${getInputFile(VarTool.relKind,data.id).unixPath}' --relOut ${relPdfFile.unixPath}
           |convert  -density 300 -background white -alpha remove -alpha off ${absPdfFile.getAbsoluteFile.unixPath} ${absPngFile.getAbsoluteFile.unixPath}
           |convert  -density 300 -background white -alpha remove -alpha off ${relPdfFile.getAbsoluteFile.unixPath} ${relPngFile.getAbsoluteFile.unixPath}
           |""".stripMargin

      val commandExec = CommandExec().exec { b =>
        CommandData(tmpDir, command)
      }
      val json = if (commandExec.isSuccess) {
        Json.obj("valid" -> true, "absBase64" -> absPngFile.base64, "absPdfBase64" -> absPdfFile.base64,
          "relBase64"->relPngFile.base64,"relPdfBase64"->relPdfFile.base64)
      } else {
        Json.obj("valid" -> false, "message" -> commandExec.errorInfo)
      }
      Tool.deleteDirectory(tmpDir)
      Ok(json)
  }

  def getComparisonImage = Action {
    implicit request =>
      val data = FormTool.comparisonForm.bindFromRequest().get
      val tmpDir = Tool.createTempDirectory("tmpDir")
      val absPdfFile = new File(tmpDir, "abs_out.pdf")
      val relPdfFile = new File(tmpDir, "rel_out.pdf")
      val absPngFile = new File(tmpDir, "abs_out.png")
      val relPngFile = new File(tmpDir, "rel_out.png")
      val cStr = List(data.kindA, data.kindB).mkString(",")
      val command =
        s"""
           |${"Rscript".condaPath()} ${Tool.rPath.unixPath}/detail_cell_boxplot2.R --t '${data.id}' --m ${data.method} --c '${cStr}' --absInput '${getInputFile(VarTool.absKind,data.id).unixPath}' --absOut ${absPdfFile.unixPath} --relInput '${getInputFile(VarTool.relKind,data.id).unixPath}' --relOut ${relPdfFile.unixPath}
           |convert  -density 300 -background white -alpha remove -alpha off ${absPdfFile.getAbsoluteFile.unixPath} ${absPngFile.getAbsoluteFile.unixPath}
           |convert  -density 300 -background white -alpha remove -alpha off ${relPdfFile.getAbsoluteFile.unixPath} ${relPngFile.getAbsoluteFile.unixPath}
           |""".stripMargin

      val commandExec = CommandExec().exec { b =>
        CommandData(tmpDir, command)
      }
      val json = if (commandExec.isSuccess) {
        Json.obj("valid" -> true, "absBase64" -> absPngFile.base64, "absPdfBase64" -> absPdfFile.base64,
          "relBase64"->relPngFile.base64,"relPdfBase64"->relPdfFile.base64)
      } else {
        Json.obj("valid" -> false, "message" -> commandExec.errorInfo)
      }
      Tool.deleteDirectory(tmpDir)
      Ok(json)
  }

  def proportion = Action {
    Ok(views.html.proportion())
  }

  def getPercentImageJson = {
    val tmpDir = Tool.createTempDirectory("tmpDir")
    val bloodPdfFile = new File(tmpDir, "blood_out.pdf")
    val bloodPngFile = new File(tmpDir, "blood_out.png")
    val tissuePdfFile = new File(tmpDir, "tissue_out.pdf")
    val tissuePngFile = new File(tmpDir, "tissue_out.png")
    val command =
      s"""
         |${"Rscript".condaPath()} ${Tool.rPath.unixPath}/cell_rel_cohorts_cumulativePercent.R --b ${bloodFile.unixPath} --t ${tissueFile.unixPath} --bo ${bloodPdfFile.unixPath} --to ${tissuePdfFile.unixPath}
         |convert  -density 300 -background white -alpha remove -alpha off ${bloodPdfFile.getAbsoluteFile.unixPath} ${bloodPngFile.getAbsoluteFile.unixPath}
         |convert  -density 300 -background white -alpha remove -alpha off ${tissuePdfFile.getAbsoluteFile.unixPath} ${tissuePngFile.getAbsoluteFile.unixPath}
         |""".stripMargin

    val commandExec = CommandExec().exec { b =>
      CommandData(tmpDir, command)
    }
    val json = if (commandExec.isSuccess) {
      Json.obj("valid" -> true, "bloodBase64" -> bloodPngFile.base64, "bloodPdfBase64" -> bloodPdfFile.base64,
        "tissueBase64" -> tissuePngFile.base64, "tissuePdfBase64" -> tissuePdfFile.base64)
    } else {
      Json.obj("valid" -> false, "message" -> commandExec.errorInfo)
    }
    Tool.deleteDirectory(tmpDir)
    json
  }

  def getAllDiffGroup = Action.async {
    browseCellDao.selectAllDiffGroup.map { x =>
      val types = x.flatMap { x =>
        x.mySplit(",")
      }.map(_.trim).distinct.notEmptyLines.sorted
      val finalTypes = if (types.containsIgnoreCase("NA")) {
        types.find(_.equalsIgnoreCase("NA")).get :: types.filterNot(x => "NA".equalsIgnoreCase(x))
      } else types
      Ok(Json.toJson(finalTypes))
    }
  }

  def getPercentImage = Action {
    implicit request =>
      Ok(getPercentImageJson)
  }

  def getCorHeatmapJson(data: ProportionData) = {
    val kind = data.kind.get
    val bloodFile = new File(Tool.dataDir, s"cohorts/${kind}_cell_rel_blood.csv")
    val tissueFile = new File(Tool.dataDir, s"cohorts/${kind}_cell_rel_tissue.csv")
    val tmpDir = Tool.createTempDirectory("tmpDir")
    val bloodPdfFile = new File(tmpDir, "blood_out.pdf")
    val bloodPngFile = new File(tmpDir, "blood_out.png")
    val tissuePdfFile = new File(tmpDir, "tissue_out.pdf")
    val tissuePngFile = new File(tmpDir, "tissue_out.png")
    val command =
      s"""
         |${"Rscript".condaPath()} ${Tool.rPath.unixPath}/cell_rel_cohort_cumulativePercent.R --b ${bloodFile.unixPath} --t ${tissueFile.unixPath} --bo ${bloodPdfFile.unixPath} --to ${tissuePdfFile.unixPath}
         |convert  -density 300 -background white -alpha remove -alpha off ${bloodPdfFile.getAbsoluteFile.unixPath} ${bloodPngFile.getAbsoluteFile.unixPath}
         |convert  -density 300 -background white -alpha remove -alpha off ${tissuePdfFile.getAbsoluteFile.unixPath} ${tissuePngFile.getAbsoluteFile.unixPath}
         |""".stripMargin

    val commandExec = CommandExec().exec { b =>
      CommandData(tmpDir, command)
    }
    val json = if (commandExec.isSuccess) {
      Json.obj("valid" -> true, "bloodBase64" -> bloodPngFile.base64, "bloodPdfBase64" -> bloodPdfFile.base64,
        "tissueBase64" -> tissuePngFile.base64, "tissuePdfBase64" -> tissuePdfFile.base64)
    } else {
      Json.obj("valid" -> false, "message" -> commandExec.errorInfo)
    }
    Tool.deleteDirectory(tmpDir)
    json
  }

  def getSampleImageJson(data: ProportionData) = {
    val kind = data.kind.get
    val bloodFile = new File(Tool.dataDir, s"cohorts/${kind}_cell_rel_blood.csv")
    val tissueFile = new File(Tool.dataDir, s"cohorts/${kind}_cell_rel_tissue.csv")
    val tmpDir = Tool.createTempDirectory("tmpDir")
    val bloodPdfFile = new File(tmpDir, "blood_out.pdf")
    val bloodPngFile = new File(tmpDir, "blood_out.png")
    val tissuePdfFile = new File(tmpDir, "tissue_out.pdf")
    val tissuePngFile = new File(tmpDir, "tissue_out.png")
    val command =
      s"""
         |${"Rscript".condaPath()} ${Tool.rPath.unixPath}/cell_rel_sample_rosePolar.R --b ${bloodFile.unixPath} --t ${tissueFile.unixPath} --s ${data.sample.get} --bo ${bloodPdfFile.unixPath} --to ${tissuePdfFile.unixPath}
         |convert  -density 300 -background white -alpha remove -alpha off ${bloodPdfFile.getAbsoluteFile.unixPath} ${bloodPngFile.getAbsoluteFile.unixPath}
         |convert  -density 300 -background white -alpha remove -alpha off ${tissuePdfFile.getAbsoluteFile.unixPath} ${tissuePngFile.getAbsoluteFile.unixPath}
         |""".stripMargin
    val commandExec = CommandExec().exec { b =>
      CommandData(tmpDir, command)
    }
    val json = if (commandExec.isSuccess) {
      Json.obj("valid" -> true, "bloodBase64" -> bloodPngFile.base64, "bloodPdfBase64" -> bloodPdfFile.base64,
        "tissueBase64" -> tissuePngFile.base64, "tissuePdfBase64" -> tissuePdfFile.base64)
    } else {
      Json.obj("valid" -> false, "message" -> commandExec.errorInfo)
    }
    Tool.deleteDirectory(tmpDir)
    json
  }

  def getCorHeatmapImage = Action {
    implicit request =>
      val data = FormTool.proportionForm.bindFromRequest().get
      Ok(getCorHeatmapJson(data))
  }

  def getAllDataset: Action[AnyContent] = Action { implicit request =>
    val dir = new File(Tool.dataDir, "cohorts")
    val array = dir.listFiles().map { file =>
      file.getName.mySplit("_").head
    }.distinct
    Ok(Json.toJson(array))
  }

  def getImage = Action { implicit request =>
    val data = FormTool.proportionForm.bindFromRequest().get
    val json = data.kind.map { x =>
      data.sample.map { x =>
        getSampleImageJson(data)
      }.getOrElse {
        getCorHeatmapJson(data)
      }
    }.getOrElse {
      getPercentImageJson
    }
    Ok(json)
  }

  def getSelectedDataset = Action { implicit request =>
    val dir = new File(Tool.dataDir, "cohorts")
    val data = FormTool.proportionForm.bindFromRequest().get
    val maps=data.kind.map { kind =>
      val file = new File(dir, s"${kind}_cell_rel_browse.csv")
     file.csvLines.lineSeqMap.map{map=>
       map.map{case(k,v)=>
         (k.replaceAll("\\.","_"),v)
       }
     }
    }.getOrElse {
     List[SeqMap[String,String]]()
    }
    val json = Json.toJson(maps)
    Ok(json)
  }

  def getAllSample: Action[AnyContent] = Action { implicit request =>
    val data = FormTool.proportionForm.bindFromRequest().get
    val dir = new File(Tool.dataDir, "cohorts")
    val json = data.kind.map { kind =>
      val file = new File(dir, s"${kind}_cell_rel_browse.csv")
      val array = file.csvLines.lineSeqMap.map(_ ("sample"))
      Json.toJson(array)
    }.getOrElse {
      Json.toJson(List[String]())
    }
    Ok(Json.toJson(json))
  }


}
